{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "from local_plot import *\n",
    "from utils import *\n",
    "from trajectory import *\n",
    "import re\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "\n",
    "#Apply default style to all plots\n",
    "plt.style.use('seaborn-whitegrid')\n",
    "#Set default font size\n",
    "plt.rcParams.update({'font.size': 25})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def extract_average_iter_time(log_content):\n",
    "    # Sample log content:\n",
    "    # [D2VINS::solveDist@5](6364) odom Pose T [-1.029,-0.423,+0.135] YPR [+109.4,+0.1,+0.4] Vel 0.55 -0.25 0.87@ref1 landmarks 195/195 v_mea 923/1500 drone_num 5 opti_time 8.5ms steps 5 td 0.0ms \n",
    "    # Extract average opti_time using and steps using regular expression\n",
    "    pattern = re.compile(r\"landmarks \\d+/\\d+ v_mea \\d+/\\d+ drone_num \\d+ opti_time (\\d+.\\d+)ms steps (\\d+) td -?\\d+.\\d+ms\")\n",
    "    matches = re.findall(pattern, log_content)\n",
    "    opti_time = np.array([float(match[0]) for match in matches])\n",
    "    steps = np.array([float(match[1]) for match in matches])\n",
    "    return opti_time / steps\n",
    "\n",
    "\n",
    "def extract_marginalization_time(log_content):\n",
    "    # Sample log content:\n",
    "    # [D2VINS::marginalize] time cost 10.0ms frame_id 5001166 total_eff_state_dim: 209 keep_size 124 remove size 85 eff_residual_size: 845 keep_block_size 21 \n",
    "    # Extract marginalization time using regular expression\n",
    "    pattern = re.compile(r\"\\[D2VINS::marginalize\\] time cost (\\d+.\\d+)ms frame_id \\d+ total_eff_state_dim: \\d+ keep_size \\d+ remove size \\d+ eff_residual_size: \\d+ keep_block_size \\d+\")\n",
    "    matches = re.findall(pattern, log_content)\n",
    "    return np.array(matches).astype(float)\n",
    "    \n",
    "\n",
    "def process_log_file(log_file, verbose=False):\n",
    "    with open(log_file, \"r\") as f:\n",
    "        log_content = f.read()\n",
    "        iter_times = extract_average_iter_time(log_content)\n",
    "        marginalization_times = extract_marginalization_time(log_content)\n",
    "        if verbose:\n",
    "            print(f\"Average iteration time: {np.mean(iter_times):3.1f}ms +- {np.std(iter_times):3.1f}ms\")\n",
    "            print(f\"Average marginalization time: {np.mean(marginalization_times):3.1f}ms +- {np.std(marginalization_times):3.1f}ms\")\n",
    "    return iter_times, marginalization_times\n",
    "\n",
    "\n",
    "def process_data(datas, paths_gt, t0, figsize=(10, 5), nodes=range(1, 6), max_iter_time=15, max_marginalization_time=50, save_path=\"\"):\n",
    "    # Print and plot the average iteration time and marginalization time\n",
    "    # Initialize the figure for average iteration time\n",
    "    fig, ax = plt.subplots(figsize=figsize)\n",
    "    # ax.set_title(\"Average Iteration Time\")\n",
    "    ax.set_xlabel(\"Drone Number\")\n",
    "    ax.set_ylabel(\"Avg. Iter. Time (ms)\")\n",
    "\n",
    "    # Initialize the figure for average marginalization time\n",
    "    fig2, ax2 = plt.subplots(figsize=figsize)\n",
    "    # ax2.set_title(\"Average Marginalization Time\")\n",
    "    ax2.set_xlabel(\"Drone Number\")\n",
    "    ax2.set_ylabel(\"Avg. Margin. Time (ms)\")\n",
    "\n",
    "    # Initialize the figure for ATE translation\n",
    "    fig3, ax3 = plt.subplots(figsize=figsize)\n",
    "    ax3.set_xlabel(\"Drone Number\")\n",
    "    ax3.set_ylabel(\"Avg. $ATE_{pos}$ (m)\")\n",
    "\n",
    "    # Initialize the figure for ATE rotation\n",
    "    fig4, ax4 = plt.subplots(figsize=figsize)\n",
    "    ax4.set_xlabel(\"Drone Number\")\n",
    "    ax4.set_ylabel(\"Avg. $ATE_{rot}$ (deg)\")\n",
    "\n",
    "    # Initialize the figure for RE translation\n",
    "    fig5, ax5 = plt.subplots(figsize=figsize)\n",
    "    ax5.set_xlabel(\"Drone Number\")\n",
    "    ax5.set_ylabel(\"Avg. Relative Error (m)\")\n",
    "\n",
    "    # Initialize the figure for RE rotation\n",
    "    fig6, ax6 = plt.subplots(figsize=figsize)\n",
    "    ax6.set_xlabel(\"Drone Number\")\n",
    "    ax6.set_ylabel(\"Avg. Relative Rotation Error (deg)\")\n",
    "\n",
    "    # Iterate through all the data\n",
    "    for max_measurements, data in datas.items():\n",
    "        # Initialize the data for average iteration time\n",
    "        drone_nums = []\n",
    "        drone_nums_RE = []\n",
    "        iter_times = []\n",
    "        iter_time_stds = []\n",
    "\n",
    "        # Initialize the data for average marginalization time\n",
    "        marginalization_times = []\n",
    "        marginalization_time_stds = []\n",
    "\n",
    "        ATE_trans = []\n",
    "        ATE_rot = []\n",
    "        RE_trans = []\n",
    "        RE_rot = []\n",
    "\n",
    "        # Iterate through all the data for a specific max_measurements\n",
    "        for drone_num, path in data:\n",
    "            # Iterate through all the drones and average the time\n",
    "            _iter_times = []\n",
    "            _marginalization_times = []\n",
    "            nodes = range(1, drone_num + 1)\n",
    "            for i in nodes:\n",
    "                # Process the log file\n",
    "                log_file = os.path.join(path, f\"swarm{i}/d2slam.log\")\n",
    "                _iter_times_,  _marginalization_times_ = process_log_file(log_file)\n",
    "                # Concatenate the data\n",
    "                _iter_times = np.concatenate((_iter_times, _iter_times_))\n",
    "                _marginalization_times = np.concatenate((_marginalization_times, _marginalization_times_))\n",
    "            _iter_times = np.array(_iter_times)\n",
    "            _marginalization_times = np.array(_marginalization_times)\n",
    "            drone_nums.append(drone_num)\n",
    "            iter_times.append(np.mean(_iter_times))\n",
    "            iter_time_stds.append(np.std(_iter_times))\n",
    "            marginalization_times.append(np.mean(_marginalization_times))\n",
    "            marginalization_time_stds.append(np.std(_marginalization_times))\n",
    "\n",
    "            #Read path and calculate ATE\n",
    "            paths, _, _ = read_multi_folder(path + \"/swarm\", nodes, False, t0=t0)\n",
    "            align_paths(paths, paths_gt, True, True)\n",
    "            ate_pos, ate_ang = plot_fused_err(nodes, paths, paths_gt, show=False, output_ATE=True)\n",
    "            ATE_trans.append(ate_pos)\n",
    "            ATE_rot.append(ate_ang*180/pi)\n",
    "            if drone_num > 1:\n",
    "                re_pos, re_ang = relative_pose_err(nodes, paths, paths_gt, output_RE=True)\n",
    "                RE_trans.append(re_pos)\n",
    "                RE_rot.append(re_ang*180/pi)\n",
    "                drone_nums_RE.append(drone_num)\n",
    "            \n",
    "        # Plot the average iteration time\n",
    "        ax.errorbar(drone_nums, iter_times, yerr=iter_time_stds, label=f\"$\\\\tau_m=${max_measurements}\", fmt='o--', capsize=15)\n",
    "\n",
    "        # Plot the average marginalization time\n",
    "        ax2.errorbar(drone_nums, marginalization_times, yerr=marginalization_time_stds, label=f\"$\\\\tau_m=${max_measurements}\", fmt='o--', capsize=15)\n",
    "\n",
    "        # Plot the ATE translation\n",
    "        ax3.plot(drone_nums, ATE_trans, label=f\"$\\\\tau_m=${max_measurements}\", marker='o')\n",
    "        ax4.plot(drone_nums, ATE_rot, label=f\"$\\\\tau_m=${max_measurements}\", marker='o')\n",
    "\n",
    "        # Plot the RE translation\n",
    "        ax5.plot(drone_nums_RE, RE_trans, label=f\"$\\\\tau_m=${max_measurements}\", marker='o')\n",
    "        ax6.plot(drone_nums_RE, RE_rot, label=f\"$\\\\tau_m=${max_measurements}\", marker='o')\n",
    "\n",
    "    ax.set_ylim(0, max_iter_time)\n",
    "    ax2.set_ylim(0, max_marginalization_time)\n",
    "    ax.legend()\n",
    "    ax2.legend()\n",
    "    ax3.legend()\n",
    "    ax4.legend()\n",
    "    ax5.legend()\n",
    "    ax6.legend()\n",
    "    # Set x ticks \n",
    "    ax.set_xticks(drone_nums)\n",
    "    ax2.set_xticks(drone_nums)\n",
    "    ax3.set_xticks(drone_nums)\n",
    "    ax4.set_xticks(drone_nums)\n",
    "    ax5.set_xticks(drone_nums_RE)\n",
    "    ax6.set_xticks(drone_nums_RE)\n",
    "    # Save the figures\n",
    "    if save_path != \"\":\n",
    "        fig.savefig(save_path + \"iter_time.png\", bbox_inches='tight')\n",
    "        fig2.savefig(save_path + \"marginalization_time.png\", bbox_inches='tight')\n",
    "        fig3.savefig(save_path + \"ATE_trans.png\", bbox_inches='tight')\n",
    "        fig4.savefig(save_path + \"ATE_rot.png\", bbox_inches='tight')\n",
    "        fig5.savefig(save_path + \"RE_trans.png\", bbox_inches='tight')\n",
    "        fig6.savefig(save_path + \"RE_rot.png\", bbox_inches='tight')\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# max_measurements: [Drone Num, path]\n",
    "Datas = {\n",
    "    \"$\\\\infty$\": [(1, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_1_mea0/\"),\n",
    "    (2, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_2_mea0/\"),\n",
    "    (3, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_3_mea0/\"),\n",
    "    (4, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_4_mea0/\"),\n",
    "    (5, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_5_mea0/\"),\n",
    "    (6, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_6_mea0/\")],\n",
    "    2000: [(1, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_1_mea2000/\"),\n",
    "    (2, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_2_mea2000/\"),\n",
    "    (3, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_3_mea2000/\"),\n",
    "    (4, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_4_mea2000/\"),\n",
    "    (5, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_5_mea2000/\"),\n",
    "    (6, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_6_mea2000/\")],\n",
    "    # 1500: [(1, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_1_mea1500/\"),\n",
    "    # (2, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_2_mea1500/\"),\n",
    "    # (3, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_3_mea1500/\"),\n",
    "    # (4, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_4_mea1500/\"),\n",
    "    # (5, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_5_mea1500/\"),\n",
    "    # (6, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_6_mea1500/\")],\n",
    "    500: [(1, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_1_mea500/\"),\n",
    "    (2, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_2_mea500/\"),\n",
    "    (3, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_3_mea500/\"),\n",
    "    (4, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_4_mea500/\"),\n",
    "    (5, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_5_mea500/\"),\n",
    "    (6, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_6_mea500/\")],\n",
    "    # 1000: [\n",
    "    # (1, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_1_mea1000/\"),\n",
    "    # (2, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_2_mea1000/\"),\n",
    "    # (3, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_3_mea1000/\"),\n",
    "    # (4, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_4_mea1000/\"),\n",
    "    # (5, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_5_mea1000/\"),\n",
    "    # (6, \"/home/xuhao/data/d2slam/tum_datasets/outputs/d2vins_6_mea1000/\")],\n",
    "}\n",
    "data_folder = \"/home/xuhao/data/d2slam/tum_datasets/\"\n",
    "nodes = range(1, 7)\n",
    "paths_gt, t0 = read_paths(data_folder, nodes, prefix=\"groundtruth_\")\n",
    "process_data(Datas, paths_gt, t0, nodes=nodes, save_path=\"/home/xuhao/Dropbox/my_publications/TRO-2022-D2SLAM/figs/gen/scability_stereo_\", figsize=(8, 5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# max_measurements: [Drone Num, path]\n",
    "Datas = {\n",
    "    \"$\\\\infty$\": [\n",
    "    (1, \"/media/xuhao/Games/data/quadcam_7inch_n3_2023_1_14/outputs/d2vins_1_mea0/\"),\n",
    "    (2, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_2_mea0/\"),\n",
    "    (3, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_3_mea0/\"),\n",
    "    (4, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_4_mea0/\"),\n",
    "    (5, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_5_mea0/\"),\n",
    "    ],\n",
    "    2000: [(1, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_1_mea2000/\"),\n",
    "    (2, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_2_mea2000/\"),\n",
    "    (3, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_3_mea2000/\"),\n",
    "    (4, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_4_mea2000/\"),\n",
    "    (5, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_5_mea2000/\")],\n",
    "    # 1000: [(1, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_1_mea1000/\"),\n",
    "    # (2, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_2_mea1000/\"),\n",
    "    # (3, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_3_mea1000/\"),\n",
    "    # (4, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_4_mea1000/\"),\n",
    "    # (5, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_5_mea1000/\")],\n",
    "    # 3000: [\n",
    "    # (1, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_1_mea3000/\"),\n",
    "    # (2, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_2_mea3000/\"),\n",
    "    # (3, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_3_mea3000/\"),\n",
    "    # (4, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_4_mea3000/\"),\n",
    "    # (5, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_5_mea3000/\")],\n",
    "    500: [\n",
    "    (1, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_1_mea500/\"),\n",
    "    (2, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_2_mea500/\"),\n",
    "    (3, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_3_mea500/\"),\n",
    "    (4, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_4_mea500/\"),\n",
    "    (5, \"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/outputs/d2vins_5_mea500/\"),]\n",
    "}\n",
    "\n",
    "data_folder=\"/home/xuhao/data/d2slam/quadcam_7inch_n3_2023_1_14/\"\n",
    "nodes = range(1, 7)\n",
    "paths_gt, t0 = read_paths(data_folder, nodes, prefix=\"eight_yaw_\", suffix=\"-groundtruth.txt\")\n",
    "process_data(Datas, paths_gt, t0, nodes=nodes, max_iter_time=20, max_marginalization_time=120, save_path=\"/home/xuhao/Dropbox/my_publications/TRO-2022-D2SLAM/figs/gen/scability_omni_\", figsize=(8, 5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "sys.path.insert(0,'../d2pgo/scripts')\n",
    "from simulate_door_slam import *\n",
    "\n",
    "def evaluate_pgo_scailibity(g2o_path, g2o_input, max_steps=100, eta_k=1.45, rho_frame_T=0.39, \n",
    "            rho_frame_theta=1.556, simulate_delay_ms=0.0, verify_accuaracy=False, max_agent_num=5, dgs_max_steps=50, \n",
    "            max_solving_time=10.0, rho_rot_mat=0.09):\n",
    "    ignore_infor = False\n",
    "    pg = PoseGraph(g2o_input)\n",
    "    rot_init_times = []\n",
    "    cere_per_iters = []\n",
    "    rot_init_time_std = []\n",
    "    cere_per_iters_std = []\n",
    "    drone_nums = []\n",
    "    for agent_num in range(1, max_agent_num + 1):\n",
    "        print(f\"Initial cost: {pg.evaluate_cost():.1f} edges {len(pg.edges)} num {agent_num}\")\n",
    "        output_path=g2o_path + \"/d2pgo-rot-inited/\"\n",
    "        pgo_optimized, ret = call_d2pgo_opti(g2o_folder=g2o_input, output_folder=output_path, enable_rot_init=True, \n",
    "            max_steps=max_steps, agent_num=agent_num, ignore_infor=ignore_infor, eta_k=eta_k, rho_frame_theta=rho_frame_theta, \n",
    "            rho_frame_T=rho_frame_T, rho_rot_mat=rho_rot_mat, simulate_delay_ms=simulate_delay_ms, max_solving_time=max_solving_time)\n",
    "        rot_init_time = ret[\"rot_init_time\"]\n",
    "        cere_per_iter = ret[\"cere_per_iter\"]\n",
    "        rot_init_times.append(np.mean(rot_init_time))\n",
    "        cere_per_iters.append(np.mean(cere_per_iter))\n",
    "        rot_init_time_std.append(np.std(rot_init_time))\n",
    "        cere_per_iters_std.append(np.std(cere_per_iter))\n",
    "        drone_nums.append(agent_num)\n",
    "    return drone_nums, rot_init_times, cere_per_iters, rot_init_time_std, cere_per_iters_std\n",
    "\n",
    "def plot(drone_nums, rot_init_times, cere_per_iters, rot_init_time_std, cere_per_iters_std, figsize=(8, 5), save_path=\"\"):\n",
    "    fig, ax = plt.subplots(figsize=figsize)\n",
    "    ax.set_xlabel(\"Drone Number\")\n",
    "    ax.set_ylabel(\"Avg. Iter. Time (ms)\")\n",
    "\n",
    "    fig2, ax2 = plt.subplots(figsize=figsize)\n",
    "    ax2.set_xlabel(\"Drone Number\")\n",
    "    ax2.set_ylabel(\"Avg. RotInit. Iter. Time (ms)\")\n",
    "    ax.errorbar(drone_nums, cere_per_iters, yerr=cere_per_iters_std, fmt='o--', capsize=15)\n",
    "    ax2.errorbar(drone_nums, rot_init_times, yerr=rot_init_time_std, fmt='o--', capsize=15)\n",
    "    if save_path != \"\":\n",
    "        fig.savefig(save_path + \"arock_iter_time.png\", bbox_inches='tight')\n",
    "        fig2.savefig(save_path + \"rotinit_time.png\", bbox_inches='tight')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "g2o_path=\"/home/xuhao/data/d2slam/pgo/tum_corr_5/\"\n",
    "# g2o_path=\"/home/xuhao/data/d2slam/pgo/ri_realsense_walkaround_2022_10/\"\n",
    "g2o_input = g2o_path + \"/input/\"\n",
    "max_steps = 1000\n",
    "eta_k=1.5101010101010102\n",
    "rho_frame_T=0.4526572657265727\n",
    "rho_frame_theta=2.868058805880588\n",
    "rho_rot_mat =0.0918787878787879\n",
    "simulate_delay_ms=0.0\n",
    "max_solving_time = 20.0\n",
    "drone_nums, rot_init_times, cere_per_iters, rot_init_time_std, cere_per_iters_std = evaluate_pgo_scailibity(g2o_path, \n",
    "            g2o_input, max_steps=max_steps, eta_k=eta_k, rho_frame_T=rho_frame_T, rho_frame_theta=rho_frame_theta, \n",
    "            simulate_delay_ms=simulate_delay_ms, max_solving_time=max_solving_time, rho_rot_mat=rho_rot_mat, max_agent_num=5)\n",
    "plot(drone_nums, rot_init_times, cere_per_iters, rot_init_time_std, cere_per_iters_std, save_path=\"/home/xuhao/Dropbox/my_publications/TRO-2022-D2SLAM/figs/gen/scability_pgo_\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "08ce52785f0fedc81003ce387e097a83d6cc9494681cd746006386992005bb71"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
